Don't copy all incoming fragmented packets.
authorack@localhost.localdomain <ack@localhost.localdomain>
Tue, 15 Aug 2006 14:48:31 +0000 (15:48 +0100)
committerack@localhost.localdomain <ack@localhost.localdomain>
Tue, 15 Aug 2006 14:48:31 +0000 (15:48 +0100)
Some drivers (ie the e1000) hand up packets with the header in
the main area and the data in a fragment. Unless there are
multiple references on any of the data, we don't need to
make a full copy of those packets.
Signed-off-by: Emmanuel Ackaouy <ack@xensource.com>
linux-2.6-xen-sparse/drivers/xen/netback/netback.c

index c7c4c3d752af49219c84890c015aa83343683604..8d8e71bca9ccc45d6d9f3276d789ac2144c531fb 100644 (file)
@@ -143,6 +143,31 @@ static inline int is_xen_skb(struct sk_buff *skb)
        return (cp == skbuff_cachep);
 }
 
+/*
+ * We can flip without copying the packet unless:
+ *  1. The data is not allocated from our special cache; or
+ *  2. The main data area is shared; or
+ *  3. One or more fragments are shared; or
+ *  4. There are chained fragments.
+ */
+static inline int is_flippable_skb(struct sk_buff *skb)
+{
+       int frag;
+
+       if (!is_xen_skb(skb) || skb_cloned(skb))
+               return 0;
+
+       for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
+               if (page_count(skb_shinfo(skb)->frags[frag].page) > 1)
+                       return 0;
+       }
+
+       if (skb_shinfo(skb)->frag_list != NULL)
+               return 0;
+
+       return 1;
+}
+
 static struct sk_buff *netbk_copy_skb(struct sk_buff *skb)
 {
        struct skb_shared_info *ninfo;
@@ -152,6 +177,8 @@ static struct sk_buff *netbk_copy_skb(struct sk_buff *skb)
        int len;
        int headlen;
 
+       BUG_ON(skb_shinfo(skb)->frag_list != NULL);
+
        nskb = alloc_skb(SKB_MAX_HEAD(0), GFP_ATOMIC);
        if (unlikely(!nskb))
                goto err;
@@ -252,11 +279,10 @@ int netif_be_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        /*
         * We do not copy the packet unless:
-        *  1. The data is shared; or
+        *  1. The data -- including any in fragments -- is shared; or
         *  2. The data is not allocated from our special cache.
-        *  3. The data is fragmented.
         */
-       if (skb_cloned(skb) || skb_is_nonlinear(skb) || !is_xen_skb(skb)) {
+       if (!is_flippable_skb(skb)) {
                struct sk_buff *nskb = netbk_copy_skb(skb);
                if ( unlikely(nskb == NULL) )
                        goto drop;